//
// Created by dave on 18-7-29.
//

#include <iconv.h>
#include <ctime>
#include <iomanip>
#include <algorithm>
#include <glog/logging.h>
#include "data_pack.h"
#include "msg_body.h"
#include "data_utils.h"
#include "terminal/term_register.h"
#include "terminal/location_report.h"
#include "platform/term_register_resp.h"
#include "platform/plat_common_resp.h"

unsigned short read_word(std::vector<char>& data, std::size_t start) {
    return (static_cast<unsigned short>(data[start]) << 8) + data[start + 1];
}

unsigned int read_dword(std::vector<char>& data, std::size_t start) {
    unsigned int ret = data[start];
    for (int i = 1; i < 4; i++) {
        ret = (ret << 8) + data[start + i];
    }
    return ret;
}

std::string read_bcd(std::vector<char>& data, std::size_t start, std::size_t size) {
    std::string ret;
    for (std::size_t i = start; i < start + size; i++) {
        ret += static_cast<char>((data[i] >> 4 & 0x0F) + 48);
        ret += static_cast<char>((data[i] & 0x0F) + 48);
    }
    return ret;
}

void write_word(std::vector<char>& data, unsigned short word) {
    data.push_back(static_cast<char>(word >> 8 & 0xFF));
    data.push_back(static_cast<char>(word & 0xFF));
}

void write_dword(std::vector<char>& data, unsigned int dword) {
    data.push_back(static_cast<char>(dword >> 24 & 0xFF));
    data.push_back(static_cast<char>(dword >> 16 & 0xFF));
    data.push_back(static_cast<char>(dword >> 8 & 0xFF));
    data.push_back(static_cast<char>(dword & 0xFF));
}

void write_bytes(std::vector<char>& data, const char* ptr, size_t length) {
    for (int i = 0; i < length; i++) {
        data.push_back(ptr[i]);
    }
}

std::vector<char> get_bcd_bytes(const std::string& data) {
    std::vector<char> ret;
    for (std::size_t i = 0; i < data.size(); i += 2) {
        ret.push_back(static_cast<char>(((data[i] - 48) << 4) + data[i + 1] - 48));
    }
    return ret;
}

void write_bcd(std::vector<char>& buf, const std::string& data) {
    std::vector<char> ret = get_bcd_bytes(data);
    for_each(ret.cbegin(), ret.cend(), [&buf](char val) {
        buf.push_back(val);
    });
}

char checksum(std::vector<char>& buf) {
    char ret = buf[0];
    for (std::size_t i = 1; i < buf.size(); i++) {
        ret ^= buf[i];
    }
    return ret;
}

DataPack decode_data_pack(const char* buf, std::size_t start, std::size_t stop) {
    std::vector<char> vec;
    for (std::size_t i = start + 1; i < stop - 1; i++) {
        if (buf[i] == 0x7d && buf[i + 1] == 0x01) {
            vec.push_back(0x7d);
            i++;
        } else if (buf[i] == 0x7d && buf[i + 1] == 0x02) {
            vec.push_back(0x7e);
            i++;
        } else {
            vec.push_back(buf[i]);
        }
    }

    DataPack dp;
    dp.setMsgId(read_word(vec, 0));
    unsigned short params = read_word(vec, 2);
    dp.setEncrypted((params & (1 << 10)) != 0);
    dp.setSplit((params & (1 << 13)) != 0);
    dp.setContentSize(static_cast<unsigned short>(params & ((1 << 10) - 1)));
    dp.setSim(read_bcd(vec, 4, 6));
    dp.setSeqNo(read_word(vec, 10));

    std::size_t bodyStart = 12;
    std::size_t bodyEnd = vec.size() - 1; // 去除检验位
    if (dp.isSplit()) {
        dp.setPkgCount(read_word(vec, 12));
        dp.setPkgNo(read_word(vec, 14));
        bodyStart = 16;
    }

    switch (dp.getMsgId()) {
        case 0x0100: {
            auto body = std::make_shared<TermRegister>();
            body->decode(vec, bodyStart, bodyEnd);
            dp.setBody(body);
            break;
        }
        case 0x0200: {
            auto body = std::make_shared<LocationReport>();
            body->decode(vec, bodyStart, bodyEnd);
            dp.setBody(body);
            break;
        }
        case 0x8100: {
            auto body = std::make_shared<TermRegisterResp>();
            body->decode(vec, bodyStart, bodyEnd);
            dp.setBody(body);
            break;
        }
        case 0x8001: {
            auto body = std::make_shared<PlatCommonResp>();
            body->decode(vec, bodyStart, bodyEnd);
            dp.setBody(body);
            break;
        }
    }

    // LOG(INFO) << "解析：" << dp.getMsgId() << "|" << dp.getSeqNo();
    return dp;
}

int code_convert(char *from_charset, char *to_charset, char *inbuf, std::size_t inlen, char *outbuf, std::size_t outlen) {
    iconv_t cd;
    char **pin = &inbuf;
    char **pout = &outbuf;

    cd = iconv_open(to_charset, from_charset);
    if (cd == 0)
        return -1;
    memset(outbuf, 0, outlen);
    if (iconv(cd, pin, &inlen, pout, &outlen) == -1)
        return -1;
    iconv_close(cd);
    return 0;
}

std::string utf8_gbk(const std::string& str) {
    size_t outlen = str.size() * 2;
    char *outbuf = new char[outlen];
    int r = code_convert("utf-8", "gb2312", const_cast<char*>(str.c_str()), str.size(), outbuf, outlen);
    if (r == 0) {
        return std::string(outbuf);
    } else {
        return std::string();
    }
}

std::string gbk2utf8(const std::string& str) {
    size_t outlen = str.size() * 2;
    char *outbuf = new char[outlen];
    int r = code_convert("gb2312", "utf-8", const_cast<char*>(str.c_str()), str.size(), outbuf, outlen);
    if (r == 0) {
        return std::string(outbuf);
    } else {
        return std::string();
    }
}

time_t parse_time(const std::string& time) {
    std::tm tm = { };
    strptime(time.c_str(), "%y%m%d%H%M%S", &tm);
    return std::mktime(&tm);
}

std::string format_time(time_t t) {
    std::ostringstream str;
    str << std::put_time(std::localtime(&t), "%y%m%d%H%M%S");
    return str.str();
}
